﻿/****************************************************************************************************************
*                                                                                                               *
* Copyright (C) 2011 5173.com                                                                                   *
* This project may be copied only under the terms of the Apache License 2.0.                                    *
* Please visit the project Home Page http://bqqapicsharp.codeplex.com/ for more detail.                         *
*                                                                                                               *
****************************************************************************************************************/

namespace BQQAPIClient.Core.Protocol
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using BQQAPIClient.Core.Utility;

    internal class AbstractNotifyPackage
    {
        private static Dictionary<Type, NotifyPackageAttribute> cmdTable = new Dictionary<Type, NotifyPackageAttribute>();

        internal Header header;
        internal ExtraHeader extraHeader;
        internal TLVList otherData;

        public static AbstractNotifyPackage Instance
        {
            get
            {
                return new AbstractNotifyPackage();
            }
        }

        static AbstractNotifyPackage()
        {
            var assembly = typeof(AbstractNotifyPackage).Assembly;
            var packages = assembly.GetTypes().Where(type => typeof(IParseOtherData).IsAssignableFrom(type)).ToList();
            packages.ForEach(type =>
            {
                var attribute = type.GetCustomAttributes(typeof(NotifyPackageAttribute), false).FirstOrDefault();
                if (attribute != null)
                    cmdTable.Add(type, (NotifyPackageAttribute)attribute);
            });
        }

        internal IParseOtherData GetPackage(byte[] data, int offset)
        {
            var headerBytes = new byte[6];
            Buffer.BlockCopy(data, offset, headerBytes, 0, headerBytes.Length);
            header = headerBytes.BytesToStruct<Header>();

            List<KeyValuePair<Type, NotifyPackageAttribute>> pair;

            if (header.usExtraLen == 7)
            {
                otherData = new TLVList();
                var index = offset + headerBytes.Length;
                var tlv = TLV.GetTLV(data, ref index);
                otherData.Add(tlv);
                pair = cmdTable.Where(item => item.Value.Cmd == header.usCmd).ToList();
            }
            else if (header.usExtraLen == 0)
            {
                pair = cmdTable.Where(item => item.Value.Cmd == header.usCmd).ToList();
            }
            else
            {
                var extraHeaderBytes = new byte[12];
                Buffer.BlockCopy(data, offset + headerBytes.Length, extraHeaderBytes, 0, extraHeaderBytes.Length);
                extraHeader = extraHeaderBytes.BytesToStruct<ExtraHeader>();

                otherData = new TLVList();
                var remainBytes = extraHeader.usOtherLen;
                var index = extraHeaderBytes.Length + headerBytes.Length + offset;
                while (remainBytes > 0)
                {
                    var tlv = TLV.GetTLV(data, ref index);
                    otherData.Add(tlv);
                    remainBytes -= tlv.Length;
                }
                pair = cmdTable.Where(item => item.Value.Cmd == header.usCmd && item.Value.SubCmd == extraHeader.usSubCmd).ToList();
            }

            pair = cmdTable.Where(item => item.Value.Cmd == header.usCmd && item.Value.SubCmd == extraHeader.usSubCmd).ToList();
            if (pair.Count > 0) return Activator.CreateInstance(pair.First().Key, this) as IParseOtherData;
            return null;
        }
    }
}
